﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using YTCMS.Core;
using YTCMS.Core.YTModelBinder;

namespace YTCMS.Data.Infrastructure
{
    public abstract class RepositoryBase<T> where T : class
    {
        private YTContext dataContext;
        private readonly IDbSet<T> dbset;
        protected RepositoryBase(IDatabaseFactory databaseFactory)
        {
            DatabaseFactory = databaseFactory;
            dbset = DataContext.Set<T>();
        }

        protected IDatabaseFactory DatabaseFactory
        {
            get;
            private set;
        }

        protected YTContext DataContext
        {
            get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
        }
        public virtual void Add(T entity)
        {
            dbset.Add(entity);
        }
        public virtual void Update(T entity)
        {
            dbset.Attach(entity);
            dataContext.Entry(entity).State = EntityState.Modified;
        }
        public virtual void Update(Expression<Func<T, bool>> where)
        {
            IEnumerable<T> objects = dbset.Where<T>(where).AsEnumerable();
            foreach (T obj in objects)
            {
                dbset.Attach(obj);
                dataContext.Entry(obj).State = EntityState.Modified;
            }
        }
        public virtual void Delete(T entity)
        {
            dbset.Remove(entity);
        }
        public virtual void Delete(Expression<Func<T, bool>> where)
        {
            IEnumerable<T> objects = dbset.Where<T>(where).AsEnumerable();
            foreach (T obj in objects)
                dbset.Remove(obj);
        }
        public virtual T GetById(long id)
        {
            return dbset.Find(id);
        }
        public virtual T GetById(string id)
        {
            return dbset.Find(id);
        }
        public virtual IEnumerable<T> GetAll()
        {
            return dbset;
        }
        public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
        {
            if (where == null)
                return dbset;
            var db = dbset.Where(where);
            db = GetOrderBy(db);
            return db;
        }
        public virtual IQueryable<T> GetMany(Expression<Func<T, bool>> where, Order orderBy, string orderFile)
        {
            IQueryable<T> db = null;
            if (where == null)
                db = dbset;
            else
            {
                db = dbset.Where(where);
            }
            var p = Expression.Parameter(typeof(T), "s");
            var s = typeof(T).GetProperty(orderFile);
            var m = Expression.MakeMemberAccess(p, s);
            var func = Expression.Lambda(m, p);
            switch (orderBy)
            {
                case Order.desc:
                    db = Queryable.OrderByDescending(db, (dynamic)func);
                    break;
                case Order.asc:
                    db = Queryable.OrderBy(db, (dynamic)func);
                    break;
                default:
                    break;
            }

            return db;
        }

        public T Get(Expression<Func<T, bool>> where)
        {
            var query = dbset.Where(where);

            return query.FirstOrDefault<T>();
        }
        public T Get(Expression<Func<T, bool>> where, bool NoTracking)
        {
            return dbset.Where(where).AsNoTracking().FirstOrDefault<T>();
        }

        public virtual PagedList<T> GetPageList(IQueryable<T> source, int pageIndex, int pageSize, out int pcount)
        {
            return new PagedList<T>(source, pageIndex, pageSize, out pcount);
        }
        public IEnumerable<T> ExecuteSqlCommand(string sql)
        {
            return dataContext.Database.SqlQuery<T>(sql);
        }

        public void ExecuteSql(string sql)
        {
            dataContext.Database.ExecuteSqlCommand(sql);
        }
        public void ExecuteSql(string sql, object[] parameters)
        {
            dataContext.Database.ExecuteSqlCommand(sql, parameters);
        }

        public virtual PagedList<T> GetPageList(int pageIndex, int pageSize, out int pcount)
        {
            return new PagedList<T>(dbset, pageIndex, pageSize, out pcount);
        }

        public virtual PagedList<T> PageList(SearchModel searchModel, int pageIndex, int pageSize, out int pcount)
        {
            var v = dbset.Where(searchModel);
            v = GetOrderBy(v);
            return new PagedList<T>(v, pageIndex, pageSize, out pcount);
        }
        public PagedList<T> GetPageList(Expression<Func<T, bool>> where, int pageIndex, int pageSize, out int pcount)
        {
            var db = dbset.Where(where);
            db = GetOrderBy(db);
            return new PagedList<T>(db, pageIndex, pageSize, out pcount);
        }
        //public PagedList<T> GetPageList(Expression<Func<T, bool>> where, int pageIndex, int pageSize, out int pcount)


        private static IQueryable<T> GetOrderBy(IQueryable<T> db)
        {
            var properties = typeof(T).GetProperties();
            foreach (var item in properties)
            {
                var attr = item.GetCustomAttributes(typeof(OrderAttribute), true);
                if (attr.Length > 0)
                {
                    Type ty = item.PropertyType;
                    OrderAttribute orderAttribute = attr[0] as OrderAttribute;
                    var p = Expression.Parameter(typeof(T), "s");
                    var s = Expression.Constant(orderAttribute.orderitem);
                    var makr = Expression.MakeMemberAccess(p, item);
                    var func = Expression.Lambda(makr, p);
                    switch (orderAttribute.orderType.ToLower())
                    {
                        case "desc":
                            db = Queryable.OrderByDescending(db, (dynamic)func);
                            break;
                        case "asc":
                            db = Queryable.OrderBy(db, (dynamic)func);
                            break;
                        default:
                            break;
                    }
                }
            }
            return db;
        }

        public IEnumerable<T> GetMany(Expression<Func<T, bool>> where, string includeName)
        {
            return dbset.Include(includeName).Where(where).ToList();
        }
        public IEnumerable<T> GetMany(bool noTracking, Expression<Func<T, bool>> where)
        {
            return dbset.AsNoTracking().Where<T>(where);
        }

        public object ExecuteScalar(string sql)
        {

            var r = dataContext.Database.SqlQuery<int>(sql).First();

            return r;
        }

        public int ExecuteSqlCommand(string sql, params object[] obj)
        {
            return dataContext.Database.ExecuteSqlCommand(sql, obj);
        }
        public SqlDataReader ExecuteSqlCommand(string procName, int pageIndex, int pageSize, int userid)
        {
            // return dataContext.Database.SqlQuery<T>(sql ,parameters);
            dataContext.Database.Connection.Open();
            SqlCommand cmd2 = new SqlCommand();
            cmd2.CommandType = CommandType.StoredProcedure;
            cmd2.CommandText = procName;
            cmd2.Connection = dataContext.Database.Connection as SqlConnection;
            cmd2.Parameters.Clear();
            cmd2.Parameters.AddWithValue("?userid", userid);
            cmd2.Parameters.AddWithValue("?pageIndex", pageIndex);
            cmd2.Parameters.AddWithValue("?pageSize", pageSize);
            var q = cmd2.ExecuteReader();
            return q;
        }

        public IEnumerable<T> ExecuteStoreQuery(string sql, object[] parameters)
        {

            //dataContext.ExecuteStoreQuery<T>(sql, parameters).ToList();

            var data = ((IObjectContextAdapter)dataContext).ObjectContext.ExecuteStoreQuery<T>(sql).ToList();

            return data;
        }

        public List<T> ExecuteStoredProcedure(string storedProcedureName, params object[] parameters)
        {
            return dataContext.ExecuteStoredProcedure<T>(storedProcedureName, parameters);
        }

        public IEnumerable<T> GetAsyncMany(Expression<Func<T, bool>> where)
        {
            var db = dbset.Where(where).ToListAsync<T>().Result;
            return db;
        }

        public T GetAsync(Expression<Func<T, bool>> where)
        {
            return dbset.Where(where).FirstOrDefaultAsync<T>().Result;
        }
    }
}
